package controllers

import config.{MyAwait, MyRow, MyString}
import dao.GenomeDao

import javax.inject.Inject
import play.api.data.Form
import play.api.data.Forms._
import play.api.libs.json.Json
import play.api.mvc.{AbstractController, ControllerComponents}
import utils.TableUtils
import utils.TableUtils.pageForm

import scala.concurrent.ExecutionContext
import scala.math.log10

class RNASeqController @Inject()(genomeDao: GenomeDao,cc: ControllerComponents)
                                (implicit exec: ExecutionContext) extends AbstractController(cc) with MyRow with MyAwait with MyString{

  def rnaSeqPage = Action { implicit request =>
    Ok(views.html.rna.rnaSeqPage())
  }

  def getHeader(rna: String) = Action { implicit request =>
    Ok(Json.toJson(TableUtils.rnaHeader(rna)))
  }

  def getData(rna: String) = Action { implicit request =>
    val page = pageForm.bindFromRequest.get
    val data = TableUtils.rnaRows(rna)
    val orderX = TableUtils.dealMapDataByPage(data, page)
    val total = orderX.size
    val tmpX = orderX.slice(page.offset, page.offset + page.limit)
    Ok(Json.obj("rows" -> tmpX, "total" -> total))
  }

  def getRnaSeqData(geneid: String) = Action {
    val rna = TableUtils.rna.get(geneid)
    Ok(
      if (rna.isEmpty) {
        Json.obj("exist" -> "false")
      } else {
        val data = rna.get.toSeq.flatMap { x =>
          x._2.map { y =>
            (y._1, x._1, y._2)
          }
        }.groupBy(_._1).map { x => x._1 -> x._2.map(y => (y._2 -> y._3)) }
        Json.obj("exist" -> "true", "datas" -> data, "header" -> TableUtils.rnaHeader)
      }
    )
  }

  case class RNAData(treat: String)

  val RNAForm = Form(
    mapping(
      "treat" -> text
    )(RNAData.apply)(RNAData.unapply)
  )

  def downloadRNASeq = Action { implicit request =>
    val data = RNAForm.bindFromRequest.get
    val treats = data.treat.trim.split(",").map(_.trim)
    val header = "Treatment\t" + (1 to 18).map { x =>
      TableUtils.rnaHeader(x.toString).tail.mkString("\t")
    }.mkString("\t")
    val tbody = treats.map { x =>
      val geneid = x.split('.').init.mkString(".")
      if (TableUtils.rna.contains(geneid)) {
        val rna = TableUtils.rna(geneid)(x)
        x + "\t" + rna.sortBy(_._1.toInt).map(z => z._2.mkString("\t")).mkString("\t")
      } else {
        x
      }
    }.mkString("\n")
    Ok(Json.toJson(header + "\n" + tbody))
  }

  case class ExprData(ids: String)

  val ExprForm = Form(
    mapping(
      "ids" -> text
    )(ExprData.apply)(ExprData.unapply)
  )

  case class ExprRangeData(chr: String, start: Int, end: Int)

  val ExprRangeForm = Form(
    mapping(
      "chr" -> text,
      "start" -> number,
      "end" -> number
    )(ExprRangeData.apply)(ExprRangeData.unapply)
  )

  def getGeneExprData(formId: String) = Action { implicit request =>
    val geneids = formId match {
      case "geneIdForm" => ExprForm.bindFromRequest.get.ids.trim.split(",").map(_.trim).toSeq
      case "rangeForm" =>
        val data = ExprRangeForm.bindFromRequest.get
        genomeDao.getByRange("ZMNO", data.chr, data.start, data.end).toAwait.map(_.geneid).toSeq
    }
    val rna = geneids.map { x =>
      x -> TableUtils.rna.get(x)
    }.filter(_._2.nonEmpty).map { x =>
      val geneid = x._1
      val data = x._2.get.head._2.sortBy(_._1.toInt).flatMap(_._2)
      geneid -> data
    }

    val table = rna.map{x=> (x._1 +: x._2.map(_.toString)).zipWithIndex.map{y=>y._2.toString -> y._1}.toMap}

    val header = "Gene ID" +: TableUtils.rnaHeader.toSeq.filter(_._1.isDouble).sortBy(_._1.toInt).flatMap(_._2.tail)

    val heatmap = {
      val data ="GeneID,mRNA,EXP\n" + rna.zipWithIndex.flatMap {x=>
        x._1._2.zipWithIndex.map{y=>
          y._2 + ","+x._2 + ","+ log2(y._1)
        }
      }.mkString("\n")
      val xAxis = header.tail
      val yAxis = rna.map(_._1)
      Json.obj("xAxis" -> xAxis,"yAxis" -> yAxis,"csv" -> data)
    }

    val line = {
      val data = rna.map { x =>
        Json.obj("name" -> x._1,"data" -> x._2)
      }
      val xAxis = header.tail

      Json.obj("xAxis" -> xAxis ,"series" -> data)

    }

    Ok(Json.obj("table" -> table,"header" -> header,"heatmap" -> heatmap,"line" -> line))
  }

  def log2(x: Double): Double = log10(x + 1) / log10(2.0)

}
